﻿/* 
 * This file is part of marvellous-components project.
 * 
 * The marvellous-components is free software: you can redistribute it 
 * and/or modify it under the terms of the GNU General Public License 
 * version 3 as published by the Free Software Foundation.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Drawing;


namespace marvellous_components.AwesomeTextBox
{
    /// <summary>
    /// TextBox with additional methods.
    /// Must be initialized with InitializeAwesomeTextbox method.
    /// Autocomplete window is autopmatically resized to fit screen.
    /// 
    /// The hideAutocompleteWindow method should be invoked on parent form hide.
    /// 
    /// The acceptCancelForm is parent form of component - AcceptButton and CancelButton 
    /// are deactivated when Autocomplete box is shown.
    /// 
    /// Available keyboard shortcuts:
    ///  - Escape - hide autocomplete list
    ///  - Ctrl + Space - extend autocomplete list
    ///  - Enter - choose current selection
    ///  - PageUp, PageDown, Up, Down - select autocomplete
    ///  - Letters, Backspace, Delete - show and search in autocomplete list
    ///  
    /// Available mouse actions:
    ///  - Double Click on autocomplete list item - choose current selection
    ///  - Double Click on textBox - shows autocomplete list
    /// </summary>
    public class AwesomeTextBox : TextBox
    {
        private AwesomeTextBoxAutocomplete autocompl;

        private Form acceptCancelForm;

        private AwesomeDataProvider autocompleteProvider;

        private bool searchEnabled;

        /// <summary>
        /// Context search. Might be slow for big amounts of data.
        /// </summary>
        public bool SearchEnabled
        {
            get { return searchEnabled; }
            set { searchEnabled = value; }
        }

        private string dataNotReadyUserMessage = "Please wait. Data not ready";

        /// <summary>
        /// Message shown to user in MessageBox when autocomplete data is not ready.
        /// </summary>
        public string DataNotReadyUserMessage
        {
            get { return dataNotReadyUserMessage; }
            set { dataNotReadyUserMessage = value; }
        }

        public void InitializeAwesomeTextbox(AwesomeDataProvider autocompleteProvider, Form acceptCancelForm)
        {
            // add events
            this.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.this_KeyPress);
            this.KeyUp += new System.Windows.Forms.KeyEventHandler(this.this_KeyUp);
            this.Leave += new System.EventHandler(this.this_Leave);
            this.DoubleClick += new System.EventHandler(this.this_DoubleClick);
            
            this.acceptCancelForm = acceptCancelForm;
            this.autocompleteProvider = autocompleteProvider;

            this.autocompl = new AwesomeTextBoxAutocomplete(this, acceptCancelForm);
            autocompl.SetItems(autocompleteProvider.getReducedData());
        }

        private void showAutocompleteWindow(bool focus)
        {
            autocompl.Location = this.PointToScreen(new Point(0,this.Height));
            autocompl.Show();
            autocompl.Activate();
            if (!focus)
            {
                this.Focus();
                if (searchEnabled)
                {
                    autocompl.Search(this.Text);
                }
            }
        }

        public void hideAutocompleteWindow()
        {
            autocompl.Hide();
            autocompl.SetItems(autocompleteProvider.getReducedData());
        }

        internal void this_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (e.KeyChar == (char)Keys.Escape)
            {
                hideAutocompleteWindow();
                e.Handled = true;
            }
            else if (((ModifierKeys & Keys.Control) == Keys.Control) && (e.KeyChar == (char)Keys.Space))
            {
                if (autocompleteProvider.isDataReady())
                {
                    autocompl.SetItems(autocompleteProvider.getData());
                    showAutocompleteWindow(false);
                }
                else
                {
                    MessageBox.Show(acceptCancelForm, dataNotReadyUserMessage);
                }
                e.Handled = true;
            }
            else if (e.KeyChar == (char)Keys.Enter)
            {
                if (autocompl.Visible)
                {
                    autocompl.pushSelectedToSlave();
                    this.SelectionStart = this.Text.Length;
                }
                e.Handled = true;
            }
            
        }

        private void this_Leave(object sender, EventArgs e)
        {
            hideAutocompleteWindow();
        }


        private void this_DoubleClick(object sender, EventArgs e)
        {
            showAutocompleteWindow(true);
        }

        private void this_KeyUp(object sender, KeyEventArgs e)
        {
            // focus to list if down arrow
            if ((Keys.Down.Equals(e.KeyCode)) || ((Keys.Up.Equals(e.KeyCode))) || ((Keys.PageDown.Equals(e.KeyCode))) || ((Keys.PageUp.Equals(e.KeyCode))))
            {
                showAutocompleteWindow(true);
                e.Handled = true;
            }
            // do search when letter or digit changed
            else if ((Char.IsLetterOrDigit(Convert.ToChar(e.KeyCode))) || (Keys.Back.Equals(e.KeyCode)) || (Keys.Delete.Equals(e.KeyCode)))
            {
                showAutocompleteWindow(false);
            }
        }

    }
}
